/**
 * The actual color-changing box that users play with.
 * @author Keilan Jackson
 * @version 1.0
 * 
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import java.util.Random;

@SuppressWarnings("serial")
public class ColoredBox extends JPanel {
	public boolean gameIsOver = false;
	public JLabel instructions, gameInfo;
	
	protected int level, score;
	protected JPanel startColorPanel, northBorder, westBorder, eastBorder, southBorder, colorPanel, results, debugPanel, startTimerPanel;
	protected Timer startTimer, gameTimer, elapsedTimer, directionCanChangeTimer, bufferTimer;
	protected int startTimerInterval, gameTimerInterval;
	protected int timeToStart;
	protected int elapsedTime;
	protected int startRed, startGreen, startBlue;
	protected int red, green, blue;
	protected int redChange, greenChange, blueChange;
	protected int redDirection, greenDirection, blueDirection;
	protected int colorTether, redUpBound, greenUpBound, blueUpBound, redLowBound, greenLowBound, blueLowBound;
	protected final int MAX_COLOR_DISTANCE = (3*255) * (3*255);
	protected double colorDistance;
	protected boolean gameTimerGoing = false;
	protected JLabel rLabel, gLabel, bLabel, elapsedLabel;
	protected JLabel rChangeLabel, gChangeLabel, bChangeLabel, rDLabel, gDLabel, bDLabel, colorDistanceLabel;
	protected JLabel rBoundsLabel, gBoundsLabel, bBoundsLabel;
	private Font globalFont;
	private int globalWidth;
		
	public ColoredBox(int l, Font f, int width) {
	//---------------------------------
	//Set default values for all variables
	//---------------------------------	
		globalFont = f;
		level = l;
		score = 0;
		globalWidth = width;
		setColorDirections();
		setColorChange();
		//sets up the startColorPanel color, within bounds

		startRed = genColor(redChange,0,255);
		startGreen = genColor(greenChange,0,255);
		startBlue = genColor(blueChange,0,255);
		
		//sets up starting rgbs for colorPanel
		colorTether = 16 * level;
		redUpBound = startRed + colorTether;  /// add/subtract 1 to help keep the colors in bounds
			if(redUpBound>255)
				redUpBound = 255;
		greenUpBound = startGreen + colorTether;
			if(greenUpBound>255)
				greenUpBound = 255;
		blueUpBound = startBlue + colorTether;
			if(blueUpBound>255)
				blueUpBound = 255;
		redLowBound = startRed - colorTether;
			if(redLowBound<0)
				redLowBound = 0;
		greenLowBound = startGreen - colorTether;
			if(greenLowBound<0)
				greenLowBound = 0;
		blueLowBound = startBlue - colorTether;
			if(blueLowBound<0)
				blueLowBound = 0;
		red = genColor(redChange,redLowBound,redUpBound);
		green = genColor(greenChange,greenLowBound,greenUpBound);
		blue = genColor(blueChange,blueLowBound,blueUpBound);
		
		elapsedTime = 0;
		startTimerInterval = 1000;
		gameTimerInterval = 30;
		timeToStart = 3; //in seconds
		
		//sets up the Timers
		startTimer = new Timer(startTimerInterval, new StartTimerListener()); //listener executes every startTimerInterval ms
		gameTimer = new Timer(gameTimerInterval, new GameTimerListener());  //listener executes every gameTimerInterval ms
		elapsedTimer = new Timer(1, new ElapsedTimerListener()); //listener executes every 1ms	
		
		//sets up the container panel with the cheat border
		startColorPanel = new JPanel();
		startColorPanel.setPreferredSize (new Dimension((globalWidth/2), (globalWidth/2)));
		startColorPanel.setBackground(new Color (startRed, startGreen, startBlue));
		startColorPanel.setLayout(new BorderLayout());
		
		northBorder = new JPanel();
		northBorder.setPreferredSize (new Dimension(25,25));
		northBorder.setBackground(new Color (startRed, startGreen, startBlue));
		
		westBorder = new JPanel();
		westBorder.setPreferredSize (new Dimension(25,25));
		westBorder.setBackground(new Color (startRed, startGreen, startBlue));
		
		eastBorder = new JPanel();
		eastBorder.setPreferredSize (new Dimension(25,25));
		eastBorder.setBackground(new Color (startRed, startGreen, startBlue));	
		
		southBorder = new JPanel();
		southBorder.setPreferredSize (new Dimension(25,25));
		southBorder.setBackground(new Color (startRed, startGreen, startBlue));	
		
		//sets up morphing panel
		colorPanel = new JPanel();
		colorPanel.setPreferredSize (new Dimension((globalWidth/2 - 50),globalWidth/2 - 50));
		//colorPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
		//colorPanel.setAlignmentY(Component.CENTER_ALIGNMENT);
		colorPanel.setBackground(new Color (red, green, blue));
		
		//startTimerPanel = new JPanel();
		//startTimerPanel.setBackground(Color.black);
		//startTimerLabel = new JLabel("" + timeToStart);
		//startTimerLabel.setForeground(Color.black);
		//startTimerPanel.setSize(50,25);
		//startTimerPanel.add (startTimerLabel);

		rLabel = new JLabel("Red: " + red);
		gLabel = new JLabel("Green: " + green);
		bLabel = new JLabel("Blue: " + blue);
		elapsedLabel = new JLabel("Elapsed Time:" + elapsedTime);
		rChangeLabel = new JLabel ("redChange: " + redChange);
		gChangeLabel = new JLabel ("greenChange: " + greenChange);
		bChangeLabel =  new JLabel ("blueChange: " + blueChange);
		rDLabel = new JLabel ("redDirection: " + redDirection);
		gDLabel = new JLabel ("greenDirection: " + greenDirection);
		bDLabel = new JLabel ("blueDirection: " + blueDirection);
		rBoundsLabel = new JLabel("redBounds: " + redLowBound + "," + redUpBound);
		gBoundsLabel = new JLabel("greenBounds: " + greenLowBound + "," + greenUpBound);
		bBoundsLabel = new JLabel("blueBounds: " + blueLowBound + "," + blueUpBound);
		
		//instructions label below colorPanel that is shared between boxpanel and gameinterface
		gameInfo = new JLabel("Countdown: " + timeToStart);
		gameInfo.setFont(globalFont);
		instructions = new JLabel("Wait for the timer.");
		instructions.setFont(globalFont);
		
		debugPanel = new JPanel();
		debugPanel.setLayout(new BoxLayout (debugPanel, BoxLayout.Y_AXIS));
		debugPanel.add (new JLabel("Current Data"));
		debugPanel.add (rLabel);
		debugPanel.add (gLabel);
		debugPanel.add (bLabel);
		debugPanel.add (elapsedLabel);
		debugPanel.add (rChangeLabel);
		debugPanel.add (gChangeLabel);
		debugPanel.add (bChangeLabel);
		debugPanel.add (rDLabel);
		debugPanel.add (gDLabel);
		debugPanel.add (bDLabel);
		debugPanel.add (new JLabel("Target red: " + startRed));
		debugPanel.add (new JLabel("Target green: " + startGreen));
		debugPanel.add (new JLabel("Target blue: " + startBlue));
		debugPanel.add (rBoundsLabel);
		debugPanel.add (gBoundsLabel);
		debugPanel.add (bBoundsLabel);
		debugPanel.add (new JLabel("Score: " + score));
		
		//add the info display and colorPanel components
		
		//northBorder.add (startTimerPanel);
		startColorPanel.add (northBorder, BorderLayout.NORTH);
		startColorPanel.add (westBorder, BorderLayout.WEST);		
		startColorPanel.add (eastBorder, BorderLayout.EAST);
		startColorPanel.add (southBorder, BorderLayout.SOUTH);	
		add (startColorPanel);
		
		startTimer.start();
	}	
	
	public void endGame() {
		gameTimer.stop();
		gameTimerGoing = false;
		elapsedTimer.stop();
		colorDistance = (Math.abs(red -startRed) + Math.abs(green - startGreen) + Math.abs(blue - startBlue)) *
						(Math.abs(red -startRed) + Math.abs(green - startGreen) + Math.abs(blue - startBlue));
		score = (int) (MAX_COLOR_DISTANCE / colorDistance) * level * level;
		gameIsOver = true;
		toggleBorder();
		gameInfo.setText("Score: " + score);
		instructions.setText("Click to continue.");
	}
	
	public void startGame() {
		startTimer.start();
	}
	
	public boolean getGameTimerStatus() {
		return gameTimerGoing;
	}
	
	//check to make sure a color is inside the [0,255) bounds
	protected boolean checkColorBounds(int color, int colorChange, int colorLowBound, int colorUpBound) {
		if(color > colorLowBound + colorChange && color < colorUpBound - colorChange)
			return true;
		return false;
	}
	
	protected int genColor(int colorChange, int colorLowBound, int colorUpBound){
		Random rgen = new Random();	
		int c = rgen.nextInt(colorUpBound - 2*colorChange - colorLowBound) + colorChange + colorLowBound;
		return c;
	}
	//sets up colorDirections to be -1 or 1
	public void setColorDirections() {
		java.util.Random rGen = new java.util.Random();
		do{
				redDirection = rGen.nextInt(3) - 1;
				greenDirection = rGen.nextInt(3) - 1;
				blueDirection = rGen.nextInt(3) - 1;
		} while(redDirection == 0 || greenDirection == 0 || blueDirection == 0);
	}
	//sets up colorChanges to be between 1 and level/2 + 1
	public void setColorChange() {
		java.util.Random rGen = new java.util.Random();
			redChange = rGen.nextInt((level/2)+1) +1;
			greenChange = rGen.nextInt((level/2)+1) +1;
			blueChange = rGen.nextInt((level/2)+1) +1;
	}

	public int getScore(){
		return score;
	}
	public JPanel getDebugPanel(){
		return debugPanel;
	}
	//toggles Border visibility
	public void toggleBorder(){
		northBorder.setVisible(!northBorder.isVisible());
		westBorder.setVisible(!westBorder.isVisible());
		eastBorder.setVisible(!eastBorder.isVisible());
		southBorder.setVisible(!southBorder.isVisible());
	}
	//sets globalWidth
	public void setGlobalWidth(int w){
		globalWidth = w;
		startColorPanel.setPreferredSize (new Dimension((globalWidth/2), (globalWidth/2)));
		colorPanel.setPreferredSize (new Dimension((globalWidth/2 - 50),globalWidth/2 - 50));
	}
	
	// this controls what is printed on the screen every repaint()
	public void paintComponent(Graphics page) {
		rLabel.setText("Red: " + red);
		gLabel.setText("Green: " + green);
		bLabel.setText("Blue: " + blue);
		rChangeLabel.setText("redChange: " + redChange);
		gChangeLabel.setText("greenChange: " + greenChange);
		bChangeLabel.setText("blueChange: " + blueChange);
		rDLabel.setText("redDirection: " + redDirection);
		gDLabel.setText("greenDirection: " + greenDirection);
		bDLabel.setText("blueDirection: " + blueDirection);
		
		colorPanel.setBackground(new Color(red, green, blue));

		super.paintComponent(page);
	}
	
	//this changes the rgb values by their Change values
	protected class GameTimerListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			red += redChange * redDirection;
			green += greenChange * greenDirection;
			blue += blueChange * blueDirection;
			// check to make sure red, green, and blue are inside the 0,255 interval
			if(!checkColorBounds(red, redChange, redLowBound, redUpBound)){
				redDirection *= -1;
				red += redChange * redDirection;
			}
			if(!checkColorBounds(green, greenChange, greenLowBound, greenUpBound)){
				greenDirection *= -1;
				green += greenChange * greenDirection;
			}
			if(!checkColorBounds(blue, blueChange, blueLowBound, blueUpBound)){
				blueDirection *= -1;
				blue += blueChange * blueDirection;
			}
			repaint();
		}
	}
	
	//this is the timer at the start of the game
	protected class StartTimerListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			if(timeToStart > 0){
				gameInfo.setText("Countdown: " +timeToStart);
				timeToStart--;
			} else {
				gameInfo.setText(" ");
				instructions.setText("Click to end game.");
				startTimer.stop();
				gameTimer.start();
				gameTimerGoing = true;
				elapsedTimer.start();
				remove (gameInfo);
				startColorPanel.add (colorPanel, BorderLayout.CENTER);
				toggleBorder();
			}
		}
	}
	
	//sets up actions that occur as the game is running
	protected class ElapsedTimerListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			elapsedTime++;
			elapsedLabel.setText("Elapsed Time: " +elapsedTime);
			if(elapsedTime % 2500 == 0){ //every 2.5 seconds
				setColorChange();
			}
		}
	}	
}
